﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Data;

namespace RecipeBookBusiness
{
    public static class ModelFacade
    {
        #region Recipe Methods
        public static DataSet SearchRecipes(int searchType, string searchValue)
        {
            DataSet searchResults = new DataSet();
            DataTable recipeTable = new DataTable("Recipes");
            recipeTable.Columns.Add("ID", typeof(int));
            recipeTable.Columns.Add("Name", typeof(string));
            recipeTable.Columns.Add("ServingSize", typeof(int));
            recipeTable.Columns.Add("UserAddedID", typeof(int));
            recipeTable.Columns.Add("Username", typeof(string));
            recipeTable.Columns.Add("CompositeRating", typeof(double));
            recipeTable.Columns.Add("CategoryList", typeof(string));
            recipeTable.Columns.Add("NutritionalInfo", typeof(string));
            recipeTable.Columns.Add("Instructions", typeof(string));
            recipeTable.Columns.Add("DateAdded", typeof(DateTime));
            
            List<Recipe> allRecipes = RecipeBook.GetRecipes();
            switch (searchType)
            {
                case(1):
                    foreach (Recipe r in allRecipes)
                    {
                        if (r.Name.ToLower().Equals(searchValue.ToLower()))
                        {
                            DataRow recipeRow = GetRowForRecipeSearch(recipeTable.NewRow(), r);
                            recipeTable.Rows.Add(recipeRow);
                        }
                    }
                    break;
                case(2):
                    //See if they searched a valid category - if not, no results will be returned
                    int categoryID = -1;
                    foreach (Category c in RecipeBook.GetCategories())
                    {
                        if (c.Name.ToLower().Equals(searchValue.ToLower()))
                        {
                            categoryID = c.ID;
                            break;
                        }
                    }

                    if (categoryID != -1)
                    {
                        foreach (Recipe r in allRecipes)
                        {
                            if (r.Categories.Contains(categoryID))
                            {
                                DataRow recipeRow = GetRowForRecipeSearch(recipeTable.NewRow(), r);
                                recipeTable.Rows.Add(recipeRow);
                            }
                        }
                    }
                    break;
                case(3):
                    //See if they searched a valid ingredient - if not, no results will be returned
                    int ingredientID = -1;
                    foreach (Ingredient i in RecipeBook.GetIngredients())
                    {
                        if (i.Description.ToLower().Equals(searchValue.ToLower()))
                        {
                            ingredientID = i.ID;
                            break;
                        }
                    }

                    if (ingredientID != -1)
                    {
                        foreach (Recipe r in allRecipes)
                        {
                            if (r.IngredientsByID.Contains(ingredientID))
                            {
                                DataRow recipeRow = GetRowForRecipeSearch(recipeTable.NewRow(), r);
                                recipeTable.Rows.Add(recipeRow);
                            }
                        }
                    }
                    break;
                case(4):
                    //See if they searched a valid ingredient - if not, no results will be returned
                    int userID = -1;
                    foreach (User u in RecipeBook.GetUsers())
                    {
                        if (u.Username.ToLower().Equals(searchValue.ToLower()))
                        {
                            userID = u.ID;
                            break;
                        }
                    }

                    if (userID != -1)
                    {
                        foreach (Recipe r in allRecipes)
                        {
                            if (r.UserAddedID.Equals(userID))
                            {
                                DataRow recipeRow = GetRowForRecipeSearch(recipeTable.NewRow(), r);
                                recipeTable.Rows.Add(recipeRow);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }

            searchResults.Tables.Add(recipeTable);
            return searchResults;
        }

        private static DataRow GetRowForRecipeSearch(DataRow recipeRow, Recipe r)
        {
            recipeRow["ID"] = r.RecipeID;
            recipeRow["Name"] = r.Name;
            recipeRow["ServingSize"] = r.ServingSize;
            recipeRow["UserAddedID"] = r.UserAddedID;
            User u = new User(r.UserAddedID);
            recipeRow["CompositeRating"] = GetCompositeRating(r.RecipeID);
            string categories = string.Empty;
            //Add marker to be replace in front end
            foreach (int c in r.Categories)
            {
                categories += (new Category(c)).Name + "^";
            }
            //Trim off the last carrot symbol
            recipeRow["CategoryList"] = categories.Substring(0, categories.Length - 1);
            recipeRow["Username"] = u.Username;
            recipeRow["NutritionalInfo"] = r.NutritionalInfo;
            recipeRow["Instructions"] = r.Instructions;
            recipeRow["DateAdded"] = r.DateAdded;
            return recipeRow;
        }

        public static DataTable GetAllRecipes()
        {
            DataTable recipeTable = new DataTable("Recipes");
            recipeTable.Columns.Add("ID", typeof(int));
            recipeTable.Columns.Add("Name", typeof(string));
            recipeTable.Columns.Add("ServingSize", typeof(int));
            recipeTable.Columns.Add("UserAddedID", typeof(int));
            recipeTable.Columns.Add("UserFullName", typeof(string));
            recipeTable.Columns.Add("Username", typeof(string));
            recipeTable.Columns.Add("NutritionalInfo", typeof(string));
            recipeTable.Columns.Add("Instructions", typeof(string));
            recipeTable.Columns.Add("DateAdded", typeof(DateTime));
            recipeTable.Columns.Add("CompositeRating", typeof(double));
            recipeTable.Columns.Add("CategoryList", typeof(string));

            foreach (Recipe r in RecipeBook.GetRecipes())
            {
                DataRow recipeRow = recipeTable.NewRow();
                recipeRow["ID"] = r.RecipeID;
                recipeRow["Name"] = r.Name;
                recipeRow["ServingSize"] = r.ServingSize;
                recipeRow["UserAddedID"] = r.UserAddedID;
                User addedUser = new User(r.UserAddedID);
                recipeRow["UserFullName"] = addedUser.FirstName + " " + addedUser.LastName;
                recipeRow["Username"] = addedUser.Username;
                recipeRow["NutritionalInfo"] = r.NutritionalInfo;
                recipeRow["Instructions"] = r.Instructions;
                recipeRow["DateAdded"] = r.DateAdded;
                recipeRow["CompositeRating"] = GetCompositeRating(r.RecipeID);
                string categories = string.Empty;
                foreach (int c in r.Categories)
                {
                    categories += (new Category(c)).Name + ", ";
                }
                //Trim off the comma and space
                recipeRow["CategoryList"] = categories.Substring(0, categories.Length - 2);

                recipeTable.Rows.Add(recipeRow);
            }

            return recipeTable;
        }

        public static DataSet GetRecipeDataSet(int recipeID)
        {
            Recipe returnRecipe = new Recipe(recipeID);
            DataSet recipeSet = new DataSet();

            DataTable recipeTable = new DataTable("Recipes");
            recipeTable.Columns.Add("ID", typeof(int));
            recipeTable.Columns.Add("Name", typeof(string));
            recipeTable.Columns.Add("ServingSize", typeof(int));
            recipeTable.Columns.Add("UserAddedID", typeof(int));
            recipeTable.Columns.Add("UserFullName", typeof(string));
            recipeTable.Columns.Add("Username", typeof(string));
            recipeTable.Columns.Add("NutritionalInfo", typeof(string));
            recipeTable.Columns.Add("Instructions", typeof(string));
            recipeTable.Columns.Add("DateAdded", typeof(DateTime));
            
            DataRow recipeRow = recipeTable.NewRow();
            recipeRow["ID"] = returnRecipe.RecipeID;
            recipeRow["Name"] = returnRecipe.Name;
            recipeRow["ServingSize"] = returnRecipe.ServingSize;
            recipeRow["UserAddedID"] = returnRecipe.UserAddedID;
            User addedUser = new User(returnRecipe.UserAddedID);
            recipeRow["UserFullName"] = addedUser.FirstName + " " + addedUser.LastName;
            recipeRow["Username"] = addedUser.Username;
            recipeRow["NutritionalInfo"] = returnRecipe.NutritionalInfo;
            recipeRow["Instructions"] = returnRecipe.Instructions;
            recipeRow["DateAdded"] = returnRecipe.DateAdded;
            recipeTable.Rows.Add(recipeRow);
            recipeSet.Tables.Add(recipeTable);

            DataTable ingredientTable = new DataTable("Ingredients");
            ingredientTable.Columns.Add("ID", typeof(int));
            ingredientTable.Columns.Add("Description", typeof(string));
            ingredientTable.Columns.Add("Amount", typeof(string));
            foreach (KeyValuePair<Ingredient, string> ingredient in returnRecipe.Ingredients)
            {
                DataRow ingredientRow = ingredientTable.NewRow();
                ingredientRow["ID"] = ingredient.Key.ID;
                ingredientRow["Description"] = ingredient.Key.Description;
                ingredientRow["Amount"] = ingredient.Value;
                ingredientTable.Rows.Add(ingredientRow);
            }
            recipeSet.Tables.Add(ingredientTable);

            DataTable equipmentTable = new DataTable("Equipment");
            equipmentTable.Columns.Add("ID", typeof(int));
            equipmentTable.Columns.Add("Description", typeof(string));
            foreach (int equipID in returnRecipe.Equipment)
            {
                Equipment equip = new Equipment(equipID);
                DataRow equipmentRow = equipmentTable.NewRow();
                equipmentRow["ID"] = equip.ID;
                equipmentRow["Description"] = equip.Description;
                equipmentTable.Rows.Add(equipmentRow);
            }
            recipeSet.Tables.Add(equipmentTable);

            DataTable categoryTable = new DataTable("Categories");
            categoryTable.Columns.Add("ID", typeof(int));
            categoryTable.Columns.Add("Name", typeof(string));
            foreach (int catID in returnRecipe.Categories)
            {
                Category cat = new Category(catID);
                DataRow categoryRow = categoryTable.NewRow();
                categoryRow["ID"] = cat.ID;
                categoryRow["Name"] = cat.Name;
                categoryTable.Rows.Add(categoryRow);
            }
            recipeSet.Tables.Add(categoryTable);

            return recipeSet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="servingSize"></param>
        /// <param name="userAddedID"></param>
        /// <param name="nutritionalInfo"></param>
        /// <param name="instructions"></param>
        /// <param name="dateAdded"></param>
        /// <param name="ingredients"></param>
        /// <returns>The ID of the newly added recipe.</returns>
        public static int AddRecipe(string name, int servingSize, int userAddedID, string nutritionalInfo, string instructions, DateTime dateAdded, List<KeyValuePair<int, string>> ingredients, List<int> equipmentIDs, List<int> categoryIDs)
        {
            //Make new recipe objects - will persist to DB
            Recipe newRecipe = new Recipe(name, servingSize, userAddedID, nutritionalInfo, instructions, dateAdded);

            //Add each ingredient to recipe object - will add to DB
            foreach (KeyValuePair<int, string> ingred in ingredients)
            {
                Ingredient ingredToAdd = new Ingredient(ingred.Key);
                newRecipe.AddIngredient(ingredToAdd, ingred.Value);
            }

            //Add each equipmentID to recipe object
            foreach (int equip in equipmentIDs)
            {
                newRecipe.AddEquipment(equip);
            }

            //Add each categoryID to recipe object
            foreach (int cat in categoryIDs)
            {
                newRecipe.AddCategory(cat);
            }

            return newRecipe.RecipeID;
        }

        //REMEMBER: Any add methods will check if it already exists in here
        public static void UpdateRecipe(int recipeID, string name, int servingSize, string nutritionalInfo, string instructions)
        {
            Recipe rToUpdate = new Recipe(recipeID);
            rToUpdate.UpdateRecipe(name, servingSize, nutritionalInfo, instructions);            
            //string retVal = "Recipe updated successfully.";
            //TODO: If error, change the retVal

            //return retVal;
        }

        public static void DeleteRecipe(int recipeID)
        {
            //TODO: Add delete flag for recipe
        }
        #endregion

        #region Category Methods
        public static DataSet GetAllCategories()
        {
            DataSet categoryDataSet = new DataSet();
            
            DataTable categoryTable = new DataTable("Categories");
            categoryTable.Columns.Add("ID", typeof(int));
            categoryTable.Columns.Add("Name", typeof(string));

            foreach (Category c in RecipeBook.GetCategories())
            {
                DataRow dr = categoryTable.NewRow();
                dr["ID"] = c.ID;
                dr["Name"] = c.Name;
                categoryTable.Rows.Add(dr);
            }

            categoryDataSet.Tables.Add(categoryTable);
            return categoryDataSet;
        }

        public static DataTable GetCategoriesExclusive(List<int> givenCategories)
        {
            DataTable categoryTable = new DataTable("Categories");
            categoryTable.Columns.Add("ID", typeof(int));
            categoryTable.Columns.Add("Name", typeof(string));
            foreach (Category e in RecipeBook.GetCategories())
            {
                if (!givenCategories.Contains(e.ID))
                {
                    DataRow edr = categoryTable.NewRow();
                    edr["ID"] = e.ID;
                    edr["Name"] = e.Name;
                    categoryTable.Rows.Add(edr);
                }
            }
            return categoryTable;
        }
        #endregion

        #region Ingredient Methods
        public static DataTable GetAllIngredients()
        {
            DataTable ingredientTable = new DataTable("Ingredients");
            ingredientTable.Columns.Add("ID", typeof(int));
            ingredientTable.Columns.Add("Description", typeof(string));
            foreach (Ingredient i in RecipeBook.GetIngredients())
            {
                DataRow idr = ingredientTable.NewRow();
                idr["ID"] = i.ID;
                idr["Description"] = i.Description;
                ingredientTable.Rows.Add(idr);
            }
            return ingredientTable;
        }

        public static DataTable GetIngredientsExclusive(List<int> givenIngreds)
        {
            if (givenIngreds.Count == 0)
            {
                return GetAllIngredients();
            }
            DataTable ingredientTable = new DataTable("Ingredients");
            ingredientTable.Columns.Add("ID", typeof(int));
            ingredientTable.Columns.Add("Description", typeof(string));
            foreach (Ingredient i in RecipeBook.GetIngredients())
            {
                if (!givenIngreds.Contains(i.ID))
                {
                    DataRow idr = ingredientTable.NewRow();
                    idr["ID"] = i.ID;
                    idr["Description"] = i.Description;
                    ingredientTable.Rows.Add(idr);
                }
            }
            return ingredientTable;
        }
        #endregion

        #region Equipment Methods
        public static DataTable GetEquipmentExclusive(List<int> givenEquipment)
        {
            DataTable equipmentTable = new DataTable("Equipment");
            equipmentTable.Columns.Add("ID", typeof(int));
            equipmentTable.Columns.Add("Description", typeof(string));
            foreach (Equipment e in RecipeBook.GetEquipment())
            {
                if (!givenEquipment.Contains(e.ID))
                {
                    DataRow edr = equipmentTable.NewRow();
                    edr["ID"] = e.ID;
                    edr["Description"] = e.Description;
                    equipmentTable.Rows.Add(edr);
                }
            }
            return equipmentTable;
        }
        #endregion

        #region User Methods
        public static bool IsUserValid(string username, string password)
        {
            foreach (User u in RecipeBook.GetUsers())
            {
                if (u.Username.ToLower().Equals(username.ToLower()) && u.Password.Equals(password))
                {
                    return true;
                }
            }
            return false;
        }

        public static int GetUserID(string username)
        {
            foreach (User u in RecipeBook.GetUsers())
            {
                if (u.Username.ToLower().Equals(username.ToLower()))
                {
                    return u.ID;
                }
            }
            return -1;
        }

        public static void AddUser(string firstName, string lastName, string email, string password, string username)
        {
            User u = new User(firstName, lastName, email, password, username);
        }

        public static bool IsUserFavorite(string username, int recipeID)
        {
            User u = GetUserByUsername(username);
            return (u.FavoriteRecipeIDs.Contains(recipeID));
        }

        public static void AddUserFavorite(string username, int recipeID)
        {
            User u = GetUserByUsername(username);
            if (!u.FavoriteRecipeIDs.Contains(recipeID))
                u.AddRecipe(recipeID);
        }

        public static void DeleteUserFavorite(string username, int recipeID)
        {
            User u = GetUserByUsername(username);
            if (u.FavoriteRecipeIDs.Contains(recipeID))
                u.RemoveRecipe(recipeID);
        }

        public static DataTable GetUserFavorites(string username)
        {
            User u = GetUserByUsername(username);
            DataTable recipeTable = new DataTable("Recipes");
            recipeTable.Columns.Add("ID", typeof(int));
            recipeTable.Columns.Add("Name", typeof(string));
            recipeTable.Columns.Add("DateAdded", typeof(DateTime));
            recipeTable.Columns.Add("CompositeRating", typeof(double));

            foreach (int recipeID in u.FavoriteRecipeIDs)
            {
                Recipe r = new Recipe(recipeID);
                DataRow recipeRow = recipeTable.NewRow();
                recipeRow["ID"] = r.RecipeID;
                recipeRow["Name"] = r.Name;
                recipeRow["DateAdded"] = r.DateAdded;
                recipeRow["CompositeRating"] = GetCompositeRating(r.RecipeID);

                recipeTable.Rows.Add(recipeRow);
            }

            return recipeTable;
        }

        public static DataTable GetOwnerRecipes(string username)
        {
            User u = GetUserByUsername(username);
            DataTable recipeTable = new DataTable("Recipes");
            recipeTable.Columns.Add("ID", typeof(int));
            recipeTable.Columns.Add("Name", typeof(string));
            recipeTable.Columns.Add("DateAdded", typeof(DateTime));

            foreach (Recipe r in RecipeBook.GetRecipes())
            {
                if (r.UserAddedID == u.ID)
                {
                    DataRow recipeRow = recipeTable.NewRow();
                    recipeRow["ID"] = r.RecipeID;
                    recipeRow["Name"] = r.Name;
                    recipeRow["DateAdded"] = r.DateAdded;

                    recipeTable.Rows.Add(recipeRow);
                }
            }

            return recipeTable;
        }

        private static User GetUserByUsername(string username)
        {
            foreach (User u in RecipeBook.GetUsers())
            {
                if (u.Username.ToLower().Equals(username.ToLower()))
                {
                    return u;
                }
            }
            return null;
        }
        #endregion

        #region Rating Methods
        public static double GetCompositeRating(int recipeID)
        {
            double totalRating = 0;
            double ratingCount = 0;

            foreach (Rating r in RecipeBook.GetRatings())
            {
                if (r.IsRecipe && r.RecipeID == recipeID)
                {
                    totalRating += r.NumericRating;
                    ratingCount++;
                }
            }

            if (ratingCount > 0)
                return (totalRating/ratingCount);
            else
                return 0;
        }

        public static void AddRating(string username, int recipeID, int commentID, bool isRecipe, int rating)
        {
            int userID = GetUserID(username);

            Rating rToDelete = null;
            foreach (Rating r in RecipeBook.GetRatings())
            {
                if (r.UserID == userID && r.RecipeID == recipeID)
                {
                    //If the rating is the same we can just break out of the function - nothing is changing
                    if (r.NumericRating == rating)
                    {
                        return;
                    }
                    else
                    {
                        rToDelete = r;
                    }
                }
            }

            if (rToDelete != null)
            {
                rToDelete.DeleteRating();
            }

            if (userID > 0)
            {
                Rating r = new Rating(userID, recipeID, commentID, isRecipe, rating);
            }
        }

        public static int CurrentUserRating(string username, int recipeID)
        {
            int userID = GetUserID(username);
            foreach (Rating r in RecipeBook.GetRatings())
            {
                if (r.UserID == userID && r.RecipeID == recipeID)
                {
                    return r.NumericRating;
                }
            }
            return -1;
        }
        #endregion

        #region Comment Methods
        public static DataTable GetCommentsByRecipeID(int recipeID)
        {
            DataTable commentTable = new DataTable("Comments");
            commentTable.Columns.Add("ID", typeof(int));
            commentTable.Columns.Add("Username", typeof(string));
            commentTable.Columns.Add("Text", typeof(string));

            foreach (Comment c in RecipeBook.GetComments())
            {
                if (c.RecipeID == recipeID)
                {
                    DataRow dr = commentTable.NewRow();
                    dr["ID"] = c.ID;
                    dr["Username"] = (new User(c.UserID)).Username;
                    dr["Text"] = c.CommentText;
                    commentTable.Rows.Add(dr);
                }
            }

            return commentTable;
        }

        public static void AddComment(int recipeID, string username, string text)
        {
            int userID = GetUserID(username);
            Comment c = new Comment(recipeID, userID, text);
        }
        #endregion
    }
}
